<?php

namespace app\command;

use app\framework\lib\Log;
use app\framework\lib\Redis;
use app\service\CurrencyKline;


class klineYesterDay extends Command
{
    // 修正Cron表达式（6字段：秒 分 时 日 月 周，此处表示每分钟执行一次）
    public $cron = "* * */1 * * *";

    // API地址常量，使用sprintf占位符更清晰
    private const API_URL = "https://api.huobi.pro/market/history/kline?symbol=%susdt&size=3&period=1day";

    // Redis键名前缀常量，便于维护
    private const REDIS_KEY_PREFIX = "market.%susdt.kline.";

    /**
     * 执行逻辑
     */
    public function run()
    {
        try {
            $this->lock(); // 加锁放在try块，确保解锁执行

            $coins = $this->getValidCoins();
            $currentDate = date("Y-m-d",strtotime("-1 day"));

            foreach ($coins as $coin) {
                $this->processCoin($coin, $currentDate);
            }
        } catch (\Exception $e) {
            Log::error("KlineSecond执行异常: " . $e->getMessage());
        } finally {
            $this->unlock(); // 确保无论成功失败都解锁
        }
    }

    /**
     * 获取并过滤有效的货币列表
     * @return array
     */
    private function getValidCoins(): array
    {
        $coins = CurrencyKline::getMarketList();
        // 过滤空值和非字符串元素
        return array_filter($coins, function ($coin) {
            return is_string($coin) && !empty(trim($coin));
        });
    }

    /**
     * 处理单个货币的K线数据
     * @param string $coin 货币代码
     * @param string $currentDate 当前日期（Y-m-d）
     */
    private function processCoin(string $coin, string $currentDate): void
    {
        $key = sprintf(self::REDIS_KEY_PREFIX . "1day", $coin);


        // 从Redis获取数据
        $data = $this->getFromRedis($key);

        // Redis无数据则从API获取
        if (empty($data)) {
            $data = self::httpGet($coin);
            // API获取失败则跳过
            if (empty($data)) {
                Log::warning("获取{$coin}数据失败，跳过处理");
                return;
            }
        }
        // 处理数据并更新昨日K线
        $this->updateYesterdayKline($data, $coin, $currentDate);
    }

    /**
     * 从Redis获取并反序列化数据
     * @param string $key
     * @return mixed
     */
    private function getFromRedis(string $key)
    {
        $serializedData = Redis::get($key);
        if (empty($serializedData)) {
            return null;
        }

        $data = @unserialize($serializedData);
        // 处理反序列化失败的情况
        if ($data === false && $serializedData !== 'b:0;') {
            Log::error("Redis数据反序列化失败，key: {$key}");
            return null;
        }
        return $data;
    }

    /**
     * 更新昨日K线数据到Redis
     * @param array $data K线数据数组
     * @param string $yesterdayKey Redis键名
     * @param string $currentDate 当前日期
     */
    private function updateYesterdayKline(array $data, string $code, string $currentDate): void
    {
        // 遍历K线数据（原array_chunk逻辑有误，直接遍历元素）
        foreach ($data as $kline) {
            // 检查数据格式合法性
            if (!isset($kline['id']) || !is_numeric($kline['id'])) {
                Log::warning("K线数据格式错误: " . json_encode($kline));
                continue;
            }
            $yesterdayKey = sprintf(self::REDIS_KEY_PREFIX . "yesterday",$code);
            // 匹配当前日期的K线数据
            if (date("Y-m-d", (int)$kline['id']) === $currentDate) {
                $kline["code"] =$code;
                Redis::set($yesterdayKey, serialize($kline));
                break; // 找到后退出循环
            }
        }
    }

    /**
     * 发送HTTP请求获取数据（使用cURL更可靠）
     * @param string $coin 货币代码
     * @return array|null 成功返回数据数组，失败返回null
     */
    public static function httpGet($coin)
    {
        $url = sprintf(self::API_URL,$coin);
        try{
            $data = file_get_contents($url);
            if (empty($data)) {
                throw new \Exception("Failed to read: $url");
            }
            $data = json_decode($data,true);;
            if ($data===false) {
                return false;
            }
            if (empty($data["status"])) {
                return;
            }
            if ($data["status"]=="ok") {
                $data["data"]["code"] = $coin;
                return $data['data'];
            }

        } catch (\Exception $e) {
            Log::error("http获取火币错误".$e->getMessage());
        }
    }
}